package com.specialized.coupon.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSONObject;
import com.specialized.background.domain.CouponTemplate;
import com.specialized.common.core.domain.Result;
import com.specialized.common.core.utils.DateUtils;
import com.specialized.common.redis.service.RedisService;
import com.specialized.common.security.utils.SecurityUtils;
import com.specialized.coupon.domain.CouponSession;
import com.specialized.coupon.domain.Vouchers;
import com.specialized.coupon.remote.CouponServiceRemote;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.specialized.coupon.mapper.SessionMapper;
import com.specialized.coupon.domain.Session;
import com.specialized.coupon.service.ISessionService;

/**
 * 抢券场次Service业务层处理
 *
 * @author djh
 * @date 2024-07-30
 */
@Service
public class SessionServiceImpl implements ISessionService {


    @Autowired
    private SessionMapper sessionMapper;

    /**
     * 查询抢券场次
     *
     * @param sessionId 抢券场次主键
     * @return 抢券场次
     */
    @Override
    public Session selectSessionBySessionId(Long sessionId) {
        return sessionMapper.selectSessionBySessionId(sessionId);
    }

    /**
     * 查询抢券场次列表
     *
     * @param session 抢券场次
     * @return 抢券场次
     */
    @Override
    public List<Session> selectSessionList(Session session) {
        return sessionMapper.selectSessionList(session);
    }

    /**
     * 新增抢券场次
     *
     * @param session 抢券场次
     * @return 结果
     */
    @Override
    public int insertSession(Session session) {
        session.setCreateTime(DateUtils.getNowDate());
        return sessionMapper.insertSession(session);
    }

    /**
     * 修改抢券场次
     *
     * @param session 抢券场次
     * @return 结果
     */
    @Override
    public int updateSession(Session session) {
        session.setUpdateTime(DateUtils.getNowDate());
        return sessionMapper.updateSession(session);
    }

    /**
     * 批量删除抢券场次
     *
     * @param sessionIds 需要删除的抢券场次主键
     * @return 结果
     */
    @Override
    public int deleteSessionBySessionIds(Long[] sessionIds) {
        return sessionMapper.deleteSessionBySessionIds(sessionIds);
    }

    /**
     * 删除抢券场次信息
     *
     * @param sessionId 抢券场次主键
     * @return 结果
     */
    @Override
    public int deleteSessionBySessionId(Long sessionId) {
        return sessionMapper.deleteSessionBySessionId(sessionId);
    }



    public static final String PREFIX = "couponsession";
    @Autowired
    private RedisService redisService;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 给用户展示
     * @return
     */
    @Override
    public List<Vouchers> findSessionCoupon() {
        //场次信息和优惠券存redis
        if (redisService.hasKey(PREFIX)) {
            Map<String, Vouchers> cacheMap = redisService.getCacheMap(PREFIX);
            List<Vouchers> vouchersArrayList = new ArrayList<>();
            for (Vouchers value : cacheMap.values()) {
                vouchersArrayList.add(value);
            }
            return vouchersArrayList;
        }
        return null;
    }

    @XxlJob("findSessionCoupon")
    public void XxlJob() {
        //优惠券场次id
        List<Vouchers> vouchersList = sessionMapper.findSessionCoupon();
        for (Vouchers vouchers : vouchersList) {
            redisService.setCacheMapValue(PREFIX,vouchers.getSessionId().toString(),vouchers);
        }
    }




    public static final String coupon = "PRODUCT:COUPON";
    public static final String ADD=
            "  if " +
                    "tonumber(redis.call('exists', KEYS[1])) == 0 then" +
                    "    redis.call('set', KEYS[1], ARGV[1])"+
                    "end";

    //商品库存redis
    @XxlJob("sessionCouponNum")
    public void sessionCouponNum() {
        DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>(ADD, Long.class);
        for (Vouchers vouchers : sessionMapper.findSessionCoupon()) {
            for (CouponTemplate couponTemplate : vouchers.getList()) {
                redisTemplate.execute(defaultRedisScript,Collections.singletonList("coupon:"+couponTemplate.getCouponId()),couponTemplate.getCount()+"");
            }
        }
    }


    //查询场次信息，判断抢券时间是否已过时
//        Result<List<CouponSession>> serviceRemoteCouponSession = couponServiceRemote.findCouponSession(couponSession);
//        List<CouponSession> couponSessionList = serviceRemoteCouponSession.getData();
//        for (CouponSession cacheMapValue : couponSessionList) {
//            Date endTime = cacheMapValue.getEndTime();
//            Date date = new Date();
//            if(endTime.getTime()<date.getTime()){
//                return Result.error("秒杀时间已过时");
//            }
//        }


    //查找优惠券状态是否为发放中
//            Result<List<CouponSession>> remoteCouponSession = couponServiceRemote.findCouponSession(couponSession);
//            List<CouponSession> couponSessionList1 = remoteCouponSession.getData();
//            for (CouponSession session : couponSessionList1) {
//                if(!session.getCouponStatus().equals(1L)){
//                    return Result.error("优惠券已经发放完了");
//                }
//                //判断库存
//                if(session.getNum()<1){
//                    return Result.error("库存不足");
//                }
//            }


    //扣减库存
//            sessionMapper.updSpikesCoupons(couponSession.getCouponSessionId());


    /**
     * lua脚本
     */
    public static final String LUA_SCRIPT =
            "if tonumber(redis.call('get', KEYS[1])) > 0 and " +
                    "tonumber(redis.call('sismember', KEYS[2], ARGV[1])) == 0 then\n" +
                    "    redis.call('incrby', KEYS[1], '-1')\n" +
                    "    redis.call('sadd', KEYS[2], ARGV[1])\n" +
                    "    return 1\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end\n";



    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CouponServiceRemote couponServiceRemote;
    public static final String COUPONTOKEN = "coupontoken";
    @Override
    public Result updSpikesCoupons(CouponSession couponSession) {
        //幂等
        if(redisService.hasKey(COUPONTOKEN+couponSession.getToken())){
            redisService.deleteObject(COUPONTOKEN+couponSession.getToken());
        }else {
            return Result.error("不能重复抢购");
        }

        //分布式锁
        RLock lock = redissonClient.getLock("couponlock" + couponSession.getCouponId());
        try {
            boolean tryLock = lock.tryLock(2, 2, TimeUnit.MINUTES);
            if(!tryLock){
                return Result.error("锁已被占用");
            }

            //查询场次信息，判断优惠券时间是否已过
            CouponSession cacheMapValue = redisService.getCacheObject(couponSession.getCouponId().toString());
            Date endTime = cacheMapValue.getEndTime();
            Date date = new Date();
            if(endTime.getTime()<date.getTime()){
                return Result.error("优惠券时间已过");
            }
            //查找优惠券状态是否为发放中
            if(!cacheMapValue.getCouponStatus().equals(1L)){
                return Result.error("优惠券已经发完了");
            }

            //通过lua脚本扣减库存和限购
            List list = new ArrayList<>();
            Long countnum = 0L;
            list.add("coupons"+couponSession.getCouponId());
            Long userId = SecurityUtils.getUserId();
            list.add("userid"+userId+couponSession.getCouponId());
            DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(LUA_SCRIPT, Long.class);
            Long execute = redisTemplate.execute(longDefaultRedisScript, list, userId + couponSession.getCouponId() + "");
            if(countnum.equals(execute)){
                return Result.error("库存不足");
            }
            //扣减库存
            cacheMapValue.setNum(couponSession.getNum()-1);
            redisService.setCacheMapValue(coupon,couponSession.getCouponId().toString(),JSONObject.toJSONString(cacheMapValue));

            return Result.success("成功");

        } catch (Exception e){
            throw new RuntimeException(e);
        } finally {
            lock.forceUnlock();
        }


    }



    @Override
    public Result getToken() {
        String uuid = UUID.randomUUID().toString();
        redisService.setCacheObject(COUPONTOKEN+uuid,1);
        return Result.success(uuid);
    }





}
